#pragma once
#include "index.hpp"
#include "Util.hpp"
#include "Jsoncpp/json.h"
#include <algorithm>

namespace Ns_Searcher
{
    class Searcher
    {
    public:
        void InitSearcher(const std::string &raw_path)
        {
            // 获取index对象
            index = Ns_Index::Index::GetInstance();
            // 根据index对象构建索引
            index->BulidIndex(raw_path);
        }

        // keyword: 搜索关键字，result: 返回给浏览器的搜索结果
        void Search(std::string &keyword, std::string *json_string)
        {
            // 1.对搜索关键字进行分词
            std::vector<std::string> phrase; // 保存分词后的"词组"
            Ns_Index::Index::InvertedZipper Index_result;
            Ns_Util::JiebaUtil::StringParticiple(keyword, &phrase); // 对搜索关键字进行分词

            // 2.根据各个"词组"(phrase), 进行index查找
            for (auto &ps : phrase)
            {
                // 词组也要进行统一小写转换，因为倒排里面也进行了统一
                boost::to_lower(ps);
                // "首先进行倒排搜索(通过"词组找到文档id")，获取到倒排拉链"
                Ns_Index::Index::InvertedZipper *itz = index->InvertedIndex(ps);
                if (itz == nullptr)
                {
                    continue; // 没有索引成功就进行下一个 -- 没有文档id就找不到文档数据
                }

                // InvertedZipper -> vector<InvertedNode>
                Index_result.insert(Index_result.end(), itz->begin(), itz->end()); // 统计倒排拉链

                // 3.汇总查找结果，按照权重值(InvertedNode.weight)进行降序排序 -- 网页呈现的是高权重到低权重的文档
                std::sort(Index_result.begin(), Index_result.end(),
                          [](const Ns_Index::InvertedNode &x, const Ns_Index::InvertedNode &y)
                          {
                              return x.weight > y.weight;
                          });

                // 4.根据查找出来的倒排拉链，构建json串 -- 通过jsoncpp库来完成序列化和反序列化
                Json::Value root;
                for (const auto &Item : Index_result)
                {
                    // 通过正排索引找到文档内容
                    Ns_Index::DockInfo *doc = index->ForwardIndex(Item.doc_id);
                    if (doc == nullptr)
                    {
                        continue;
                    }
                    // 进行序列化 -- 结构化数据 ->> 字符串
                    Json::Value elem;
                    elem["title"] = doc->title;
                    elem["desc"] = GetDesc(doc->content, Item.keyword); // 这里提取的是清洗后的全部数据，而不是摘要
                    elem["url"] = doc->url;
                    // 追加到root中
                    root.append(elem);
                }
                Json::StyledWriter sw;
                *json_string = sw.write(root); // 获取json序列化后的字符串
            }
        }

        // 获取文档的摘要 -- 摘要部分要包含搜索关键字 -- html_content: 文档内容, keyword: 搜索关键字
        std::string GetDesc(const std::string &html_content, const std::string &keyword)
        {
            // 找到第一次出现的keyword, 向前找80个字节(如果没有就从起始位置开始start), 往后找100个字节(没有就直接end)
            int prev_stop = 80;
            int next_stop = 100;

            // 因为在原始清洗过后的文档拿出的数据是没有进行大小写转换的，这里要统一小写查找
            auto it = std::search(html_content.begin(), html_content.end(), keyword.begin(),keyword.end(), 
                                    [](int x, int y)
                                    {
                                        return std::tolower(x) == std::tolower(y);
                                    });  // 从html_content中查找子串keyword第一次出现的位置，忽略大小写查找
            if (it == html_content.end())
            {
                return "None";
            }
            int pos = std::distance(html_content.begin(), it); // 返回二个迭代器之间的距离
            // 获取start，end
            int start = 0;
            int end = html_content.size() - 1;
            // 更新start，end
            if (pos - prev_stop > start)
                start = pos - prev_stop;
            if (pos + next_stop < end)
                end = pos + next_stop;
            // 返回摘要
            if (start >= end) return "None";
            return html_content.substr(start, end - start); // [)
        }

    private:
        Ns_Index::Index *index;
    };
}